# SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
# SPDX-FileContributor: Gerhard de Clercq <gerhard.declercq@kdab.com>
#
# SPDX-License-Identifier: MIT OR Apache-2.0

# TODO: does minimum version need to be set in the module as well?
# TODO: have further parameters for different options and folders etc
# TODO: will all builds want an executable? this might need to be separate
#
# APP_NAME is used as the executable name and the prefix for the lib name
# CPP_SOURCES are C++ files not generated by build.rs that we want to compile
function(cxx_qt_cmake APP_NAME CPP_SOURCES)
    cxx_qt_generate_cpp(GEN_SOURCES)

    # And specify that we want CMake to build these sources
    add_executable(${APP_NAME} "${CPP_SOURCES}" "${GEN_SOURCES}")

    cxx_qt_include(${APP_NAME})

    cxx_qt_link_rustlib(${APP_NAME})
endfunction()

# Generate the C++ sources for the given rust sources
#
# GEN_SOURCES an output variable that generated C++ files are listed into
function(cxx_qt_generate_cpp GEN_SOURCES)
    # FIXME: cxx's build.rs fails without this.
    # https://github.com/dtolnay/cxx/issues/1020
    if(APPLE)
        set(ENV{SDKROOT} ${CMAKE_OSX_SYSROOT})
    endif()
    # TODO: figure out if RelWithDebInfo is a thing in Rust and fix accordingly
    if (CMAKE_BUILD_TYPE STREQUAL "Debug")
        set(CARGO_CMD cargo build)
        set(TARGET_DIR "debug")
    else ()
        set(CARGO_CMD cargo build --release)
        set(TARGET_DIR "release")
    endif ()

    file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/target/cxx-qt-gen")

    # Run cargo during config to ensure the cpp source file list is created
    execute_process(
        COMMAND ${CARGO_CMD}
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    )

    # Now we can read the list of C++ files that cargo produced
    file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/target/cxx-qt-gen/cpp_sources.txt" CPP_GEN_SOURCES)

    # Pass the generated sources back
    set(${GEN_SOURCES} ${CPP_GEN_SOURCES} PARENT_SCOPE)
endfunction()

# Set the target include dirs of the C++ executable / library to include the rust / cxx dirs
#
# APP_NAME is the executable / library name which include dirs are being set to
function(cxx_qt_include APP_NAME)
    # FIXME: should this be target/cxx-qt-gen/include?
    target_include_directories(${APP_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
    # FIXME: should this be target/cxx-qt-gen/src ?
    target_include_directories(${APP_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/target")
    target_include_directories(${APP_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/target/cxx-qt-gen/statics")
endfunction()

# Link the generated rust library with the C++ executable / library
#
# APP_NAME is the executable / library name which the rustlib is a dependency of
function(cxx_qt_link_rustlib APP_NAME)
    # Find the threads package for the system
    set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
    find_package(Threads REQUIRED)

    if (NOT CMAKE_BUILD_TYPE)
        message(WARNING "CMAKE_BUILD_TYPE not set; building Rust in debug mode")
    endif ()
    # TODO: figure out if RelWithDebInfo is a thing in Rust and fix accordingly
    if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR NOT CMAKE_BUILD_TYPE)
        set(CARGO_CMD cargo build)
        set(TARGET_DIR "debug")
        # Hack around Rust standard library linked with Release version of C runtime
        # Refer to https://github.com/robmikh/linkerissuerepro for explanation
        if(WIN32)
            set_property(TARGET "${APP_NAME}" PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
        endif()
    else ()
        set(CARGO_CMD cargo build --release)
        set(TARGET_DIR "release")
    endif ()

    # We also list the static library produced by cargo as a dependency so that cargo gets a
    # chance to rebuild it every time that a cmake build is run.
    if(WIN32)
        set(RUST_PART_LIB "${CMAKE_CURRENT_SOURCE_DIR}/target/${TARGET_DIR}/rust.lib")
    else()
        set(RUST_PART_LIB "${CMAKE_CURRENT_SOURCE_DIR}/target/${TARGET_DIR}/librust.a")
    endif()
    add_custom_target(
        "${APP_NAME}_rustlib"
        COMMAND ${CARGO_CMD}
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    )
    add_dependencies(${APP_NAME} "${APP_NAME}_rustlib")

    # The Rust lib also needs to be linked to pthread and dl
    target_link_libraries(${APP_NAME} PRIVATE ${RUST_PART_LIB} Threads::Threads ${CMAKE_DL_LIBS})
    if(WIN32)
        target_link_libraries(${APP_NAME} PRIVATE shell32 wsock32 ws2_32 crypt32 userenv bcrypt)
    endif()
endfunction()
